/** COLORES | C: 2022-05-24 | by: Elong
 *  last update: 2022-05-31 | Ver 1.8.0 */
(function () {
  // 测试
  try {
    $(document);
  } catch (e) {
    console.warn("WARNING: Can not found JQuery in document. colores is failed to start");
    return false;
  }
  // 设置变量
  var rgbRangeMax = 1530;
  var alphaRangeMax = 255;

  // 初始化
  $(document).ready(function () {
    // console.log("ready"); // test
    $("body").append(`<div class="es-outer_package" style="display:none;">
    <div class="es-full_masker"></div>
    <div class="es-ctrl" style="color:#f00">
      <div class="es-colores_palette">
        <div class="es-colores_palette_thumb"></div>
      </div>
      <div class="es-rgb_range">
        <input class="es-rgb_range_bar" type="range" min="0" max="${rgbRangeMax}" value="0" />
        <div class="es-rgb_range_thumb"></div>
      </div>
      <div class="es-alpha_mixin">
        <div class="es-alpha_range">
          <div class="es-alpha_mask"></div>
          <input class="es-alpha_range_bar" type="range" min="0" max="${alphaRangeMax}" value="255" />
          <div class="es-alpha_range_thumb">
            <div class="es-alpha_range_thumb_before"></div>
          </div>
        </div>
        <div class="es-color_input">
          <div class="es-color_block">
            <div class="es-color_block_before"></div>
          </div>
          <input type="text" class="es-color_inputer" maxlength="9" value="#" />
        </div>
      </div>
      <div class="es-bottom_btn">
        <div class="es-cancelBtn">取消</div>
        <div class="es-confirmBtn">确认</div>
      </div>
    </div>
  </div>
  <div class="es-msgPop" style="display: none">111</div>`); // 插入主控元素
    $(".es-outer_package")
      .find(".es-full_masker,.es-cancelBtn")
      .on("click", function () {
        colores.closed();
      });
    colores.rgbMax = rgbRangeMax; // 获取色条最大值
    colores.alphaMax = alphaRangeMax; // 获取透明度最大值S
    $(".-colores-").each(function (i, e) {
      $(e).find(".es-cb").css("background", $(e).find("input[type=text]").val());
      $(e).find("input[type=text]").attr("readonly", true);
    });
  });

  // 初始化
  $(".-colores-").on("click", function (e) {
    colores.init($(this));
    var elemT = $(this).get(0).getBoundingClientRect().bottom;
    var elemL = $(this).get(0).getBoundingClientRect().left;
    $(".es-ctrl").css({ top: elemT, left: elemL });
    $(".es-outer_package").fadeIn(200); // 加载元素
  });

  var colores = {
    elem: "", // 取色器元素
    rgbMax: "",
    alphaMax: "",
    alpha: "", // 透明通道
    // 色环取色
    pure: {
      r: "",
      g: "",
      b: ""
    },
    // 调色后的颜色
    mixer: {
      r: "",
      g: "",
      b: ""
    },
    palettePos: {
      x: "",
      y: ""
    },
    init: function (elem) {
      this.elem = elem; // 注册元素
      var irbga = this.hex2rgb(elem.find("input[type=text]").val());
      this.mixer.r = irbga.r;
      this.mixer.g = irbga.g;
      this.mixer.b = irbga.b;
      this.eventOn(); // 事件监听处理
      this.addressing(false, irbga.r, irbga.g, irbga.b, irbga.a);
    },
    addressing(inputer, mR, mG, mB, alpha) {
      // 获取最大值最小值
      var max, mid, min, up, low, temp;
      mR >= mG ? ((up = { num: mR, rgb: "r" }), (low = { num: mG, rgb: "g" })) : ((up = { num: mG, rgb: "g" }), (low = { num: mR, rgb: "r" }));
      up.num >= mB ? ((max = up), (temp = { num: mB, rgb: "b" })) : ((max = { num: mB, rgb: "b" }), (temp = up));
      low.num >= temp.num ? ((mid = low), (min = temp)) : ((mid = temp), (min = low));
      // console.log(max, mid, low, up, low, temp);
      console.log("max:", max, " mid:", mid, " min:", min);

      // 色彩空间计算
      var ix = (max.num - min.num) / max.num || 0;
      var iy = (255 - max.num) / 255;
      // console.log(ix, iy); // x 与 y 值计算正确！
      this.palettePos.x = ix;
      this.palettePos.y = iy;

      $(".es-colores_palette_thumb").css({
        left: ix * $(".es-colores_palette").width(),
        top: iy * $(".es-colores_palette").height()
      });
      // console.log(`此色彩中, 为 255 的是 ${max.rgb}, 为 0 的是 ${min.rgb}`);

      // 纯色插值计算
      this.pure[min.rgb] = 0;
      this.pure[max.rgb] = 255;
      this.pure[mid.rgb] = (255 * (ix * iy - iy - ix - this.mixer[mid.rgb] / 255 + 1)) / (ix * iy - ix) || 0;
      // console.log(ix * iy - ix);
      console.log({ R: this.pure.r, G: this.pure.g, B: this.pure.b });

      // console.log(this.pure);

      // 纯色环值追踪
      var perc;
      if (this.pure.r == 255 && this.pure.g >= 0 && this.pure.b == 0) perc = this.pure.g / 1530;
      else if (this.pure.r <= 255 && this.pure.g == 255 && this.pure.b == 0) perc = (255 + (255 - this.pure.r)) / 1530;
      else if (this.pure.r == 0 && this.pure.g == 255 && this.pure.b >= 0) perc = (255 * 2 + this.pure.b) / 1530;
      else if (this.pure.r == 0 && this.pure.g <= 255 && this.pure.b == 255) perc = (255 * 3 + (255 - this.pure.g)) / 1530;
      else if (this.pure.r >= 0 && this.pure.g == 0 && this.pure.b == 255) perc = (255 * 4 + this.pure.r) / 1530;
      else if (this.pure.r == 255 && this.pure.g == 0 && this.pure.b <= 255) perc = (255 * 5 + (255 - this.pure.b)) / 1530;
      $(".es-rgb_range_thumb").css("left", perc * 100 + "%");

      // 透明度值追踪
      if (alpha) Number((this.alpha = alpha / alphaRangeMax).toFixed(4));
      else this.alpha = 1;
      $(".es-alpha_range_bar").val(this.alpha * 255);
      $(".es-alpha_range_thumb").css("left", this.alpha * 100 + "%");

      this.render(inputer); // 图层更新
    },
    eventOn: function () {
      var that = this;
      // 色环色值计算
      var rgbPure = this.pure;
      var gap = this.rgbMax / 6;
      // (val-gap*n)/gap * 255
      $(".es-rgb_range_bar").on("input", function () {
        var val = $(this).val();
        if (0 <= val && val < gap) (rgbPure.r = 255), (rgbPure.g = (val / gap) * 255), (rgbPure.b = 0);
        else if (gap <= val && val < gap * 2) (rgbPure.r = 255 - ((val - gap) / gap) * 255), (rgbPure.g = 255), (rgbPure.b = 0);
        else if (gap * 2 <= val && val < gap * 3) (rgbPure.r = 0), (rgbPure.g = 255), (rgbPure.b = ((val - gap * 2) / gap) * 255);
        else if (gap * 3 <= val && val < gap * 4) (rgbPure.r = 0), (rgbPure.g = 255 - ((val - gap * 3) / gap) * 255), (rgbPure.b = 255);
        else if (gap * 4 <= val && val < gap * 5) (rgbPure.r = ((val - gap * 4) / gap) * 255), (rgbPure.g = 0), (rgbPure.b = 255);
        else if (gap * 5 <= val && val <= that.rgbMax) (rgbPure.r = 255), (rgbPure.g = 0), (rgbPure.b = 255 - ((val - gap * 5) / gap) * 255);
        $(".es-rgb_range_thumb").css("left", (val / that.rgbMax) * 100 + "%");
        (that.mixer.r = rgbPure.r), (that.mixer.g = rgbPure.g), (that.mixer.b = rgbPure.b);
        that.paletteRGB();
        // console.log(((val / that.rgbMax) * 100).toFixed(2) + "%", val, val % 255, {
        //   r: rgbPure.r,
        //   g: rgbPure.g,
        //   b: rgbPure.b
        // });
        // if (val == that.rgbMax) {
        //   console.log(`------------------ Execute Finished ------------------`);
        //   console.log("A total of", 1350, "color values have been tested");
        //   console.log("Error:", 0, " Warn:", 0);
        // }
      });
      // 透明度控制
      $(".es-alpha_range_bar").on("input", function () {
        var val = $(this).val();
        that.alpha = Number((val / that.alphaMax).toFixed(4));
        $(".es-alpha_range_thumb").css("left", that.alpha * 100 + "%");
        that.paletteRGB();
      });
      // 调色盘事件总控器
      $(".es-colores_palette").on("mousedown touchstart", function (event) {
        var theElem = $(this);
        var temp = {
          safeX: theElem.get(0).getBoundingClientRect().left, // 元素左侧相对屏幕左侧距离
          safeY: theElem.get(0).getBoundingClientRect().top, // 元素左侧相对屏幕左侧距离
          parentH: theElem.height(),
          parentW: theElem.width()
        };
        that.thumb2D(event, theElem, temp);
        $(document).on("mousemove touchmove", function (event) {
          that.thumb2D(event, theElem, temp);
        });
        $(document).on("mouseup touchend", function () {
          $(document).unbind("mousemove touchmove");
        });
      });
      // 确认事件
      $(".es-confirmBtn").click(function () {
        that.confirm();
      });
      $(".es-color_inputer").on("input", function () {
        var erbga = that.hex2rgb($(this).val());
        if (!erbga) return false;
        that.mixer.r = erbga.r;
        that.mixer.g = erbga.g;
        that.mixer.b = erbga.b;
        that.addressing(true, erbga.r, erbga.g, erbga.b, erbga.a);
      });
      $(".es-color_inputer").on("change", function () {
        var val = $(this).val();
        var newVal = val.replace(/[^A-Fa-f0-9]/g, "");
        newVal.substring(0, 1) != "#" ? (newVal = "#" + newVal) : "";
        $(this).val(newVal);
      });
      $(document).on("keydown", function (e) {
        setTimeout(function () {
          if (e.keyCode == 13) that.confirm();
        });
      });
    },
    // 调色盘滑动监听
    thumb2D: function (event, theElem, temp) {
      var moveX = (event.touches ? event.touches[0].clientX : event.clientX) - temp.safeX;
      var moveY = (event.touches ? event.touches[0].clientY : event.clientY) - temp.safeY;
      moveX = moveX < 0 ? 0 : moveX > temp.parentW ? temp.parentW : moveX;
      moveY = moveY < 0 ? 0 : moveY > temp.parentH ? temp.parentH : moveY;
      theElem.find(".es-colores_palette_thumb").css({
        left: moveX,
        top: moveY
      });
      this.paletteRGB(moveX / temp.parentW, moveY / temp.parentH); // 获取实时百分比
    },
    // 调色盘色值计算
    paletteRGB: function (x, y) {
      x >= 0 ? (this.palettePos.x = x) : (x = this.palettePos.x);
      y >= 0 ? (this.palettePos.y = y) : (y = this.palettePos.y);
      // console.log(Number(x.toFixed(2)), Number(y.toFixed(2)));
      // console.log(this.pure.r != 255 ? "r" : "", this.pure.g != 255 ? "g" : "", this.pure.b != 255 ? "b" : "");

      this.mixer.r = (1 - y) * (this.pure.r + (255 - this.pure.r) * (1 - x));
      this.mixer.g = (1 - y) * (this.pure.g + (255 - this.pure.g) * (1 - x));
      this.mixer.b = (1 - y) * (this.pure.b + (255 - this.pure.b) * (1 - x));
      this.render();
      // console.log(this.mixer);
      // console.log(`x:${x.toFixed(2)}, y:${y.toFixed(2)}`)
      // var ttt = this.hex2rgb(this.rgb2hex(this.mixer.r, this.mixer.g, this.mixer.b, this.alpha * 255));
      // $(".testElem").text(`r:${ttt.r}, g:${ttt.g}, b:${ttt.b}`).css("background", `rgb(${this.mixer.r},${this.mixer.g},${this.mixer.b})`);
    },
    render: function (inputer) {
      if (!inputer) $(".es-color_inputer").val(this.rgb2hex(this.mixer.r, this.mixer.g, this.mixer.b, this.alpha * 255));
      $(".es-ctrl").css("color", `rgb(${this.pure.r},${this.pure.g},${this.pure.b})`);
      $(".es-alpha_mask").css("background", `linear-gradient(90deg, transparent, rgb(${this.mixer.r},${this.mixer.g},${this.mixer.b}))`);
      $(".es-colores_palette_thumb").css("color", `rgb(${this.mixer.r},${this.mixer.g},${this.mixer.b})`);
      $(".es-alpha_mixin").css("color", `rgba(${this.mixer.r},${this.mixer.g},${this.mixer.b},${this.alpha})`);
    },
    confirm: function () {
      $(document).unbind("keydown");
      var len = $(".es-color_inputer").val().length - 1;
      if (len == 3 || len == 4 || len == 6 || len == 8) {
        this.elem.find("input[type=text]").val($(".es-color_inputer").val()).trigger("change");
        this.elem.find(".es-cb").css("background", `rgba(${this.mixer.r},${this.mixer.g},${this.mixer.b},${this.alpha})`);
      } else {
        this.popup(`HEX 值格式有误 at index [${this.elem.index()}]`);
      }
      this.closed();
    },
    popup: function (x, t) {
      $(".es-msgPop").hide();
      if (!t) t = 2000;
      $(".es-msgPop").text(x);
      $(".es-msgPop").fadeIn(300);
      setTimeout(function () {
        $(".es-msgPop").fadeOut(300);
      }, t);
    },
    closed: function () {
      $(".es-outer_package").fadeOut(200);
      this.pure = { r: "", g: "", b: "" };
      this.mixer = { r: "", g: "", b: "" };
      this.palettePos = { x: "", y: "" };
      this.alpha = "";
    },
    // 由三原色值合成颜色整数值
    rgb2hex: function (R, G, B, A) {
      if (R.r && R.g && R.b) {
        var RGB = R;
        R = RGB.r;
        G = RGB.g;
        B = RGB.b;
        if (RGB.a) A = RGB.a;
      }
      (R = Number(R)), (G = Number(G)), (B = Number(B)), (A = Number(A));
      var r = (Math.round(R) < 16 ? "0" : "") + Math.round(R).toString(16),
        g = (Math.round(G) < 16 ? "0" : "") + Math.round(G).toString(16),
        b = (Math.round(B) < 16 ? "0" : "") + Math.round(B).toString(16),
        a = !isNaN(A) && A != 255 ? (Math.round(A) < 16 ? "0" : "") + Math.round(A).toString(16) : "";
      return "#" + r + g + b + a;
      // return '#' + r + g + b + a;
      // return (red * 256 * 256 + green * 256 + blue).toString(16)
    },
    hex2rgb: function (hex16) {
      if (hex16.substring(0, 1) == "#") hex16 = hex16.substring(1);
      var rgb;
      if (hex16.length == 6) {
        rgb = {
          r: parseInt("0x" + hex16.slice(0, 2)),
          g: parseInt("0x" + hex16.slice(2, 4)),
          b: parseInt("0x" + hex16.slice(4, 6))
        };
      } else if (hex16.length == 8) {
        rgb = {
          r: parseInt("0x" + hex16.slice(0, 2)),
          g: parseInt("0x" + hex16.slice(2, 4)),
          b: parseInt("0x" + hex16.slice(4, 6)),
          a: parseInt("0x" + hex16.slice(6, 8))
        };
      } else if (hex16.length == 3) {
        rgb = {
          r: parseInt("0x" + hex16.slice(0, 1) + hex16.slice(0, 1)),
          g: parseInt("0x" + hex16.slice(1, 2) + hex16.slice(1, 2)),
          b: parseInt("0x" + hex16.slice(2, 3) + hex16.slice(2, 3))
        };
      } else if (hex16.length == 4) {
        rgb = {
          r: parseInt("0x" + hex16.slice(0, 1) + hex16.slice(0, 1)),
          g: parseInt("0x" + hex16.slice(1, 2) + hex16.slice(1, 2)),
          b: parseInt("0x" + hex16.slice(2, 3) + hex16.slice(2, 3)),
          a: parseInt("0x" + hex16.slice(3, 4) + hex16.slice(3, 4))
        };
      }
      return rgb;
      // return 'rgb(' rgb.r + ',' + rgb.g + ',' + rgb.b + (rgb.a ? (',' + rgb.a) : '' ) + ')';
    }
  };
})();

/** // TODO //
 * 1. add inputer 输入框自动寻址                    √
 * 2. fix inputer 键入光标位置鬼畜问题              √
 * 3. add inputer 三位数色值 四位数值(带alpha通道)  √
 */

/**
 * 关于制作这个RGB色彩空间插件时的心得
 */

/** I 纯色 色环 RGB值的规律
 * 0  : F00    g+ =>    60 : FF0  R*  B/
 * 60 : FF0    r- =>    120: 0F0  G*  B/
 * 120: 0F0    b+ =>    180: 0FF  G*  R/
 * 180: 0FF    g- =>    240: 00F  B*  R/
 * 240: 00F    r+ =>    300: F0F  B*  G/
 * 300: F0F    b- =>    360: F00  R*  G/
 */

/* II 混色 色彩空间 明暗饱和公式 */
/* 明暗度正确 */
// this.mixer.r = (1 - y) * this.pure.r;
// this.mixer.g = (1 - y) * this.pure.g;
// this.mixer.b = (1 - y) * this.pure.b;
/* 饱和度正确 */
// this.mixer.r = (1 - y) * (255 - this.pure.r) * (1 - x);
// this.mixer.g = (1 - y) * (255 - this.pure.g) * (1 - x);
// this.mixer.b = (1 - y) * (255 - this.pure.b) * (1 - x);

// this.mixer.r = (1 - y) * (this.pure.r + (255 - this.pure.r) * (1 - x));

/** III 关于色彩空间的规律
 * Angle
 *  1. 当 x=1, y=0 时，混色全等于纯色
 *  2. 当 x=0, y=0 时，混色为纯白
 *  3. 当 x=0, y=1 或 x=1, y=1 时，混色为纯黑
 * Edge
 *  1. 当 y=0 时 (贴顶)，混色 RGB 中至少有一个值为 255
 *  2. 当 y=1 时 (贴底)，混色 RGB 三个值均为 0
 *  3. 当 x=0 时 (靠左)，混色 RGB 三个值全等且同步变化(0~255)
 *  4. 当 x=1 时 (靠右)，混色 RGB 中至少有一个值为 0
 *  5. 当 x固定 y变化 时，RGB 三个值按各自比例变化
 *  6. 当 x变化 y固定 时，RGB 中为 255 的值不变，其余二者按比例变化
 * 关于比例：
 * 设一个值为 128
 * 那么当 y 在变化时 这个值的步长为 (255 / (255 - 128)) * y + 128
 */

/** IV 混色后还原纯色 思路
 * 观察规律：一个纯色的混色空间的 R G B 中
 *  1. 有且仅有一个最大的值，其还原值为 255
 *  2. 至少有一个最小值，其还原值为 0
 *  3. 参照 (III > Edge > 1) 与 (III > Edge > 4)
 * 由此可以联想到，当我们拿到一个随机的HEX色值 却发现最大值不是255，就说明 y != 0；同样的，如果最小值不是0，也就说明 x != 1
 * 换句话说：y = 0 时，max 为 255，y 增长时数值减小，直到数值归零时 y = 1，所以 255 - max 获得的就是距离顶部的距离
 * 那么转换为色彩空间的计算，就是 y = (255 - max) / 255
 * 通过一晚上的研究我们发现，最棘手的其实还是 x 轴。但换个方向来想就简单许多。
 * 假设现在有一个随机的 RGB 色彩空间值，其中的最小值 min != 0, 如果我们不改变 y 轴，仅让他在 x 轴上平移，
 * 当 x = 0，RGB 三个值相等 且 全等于 max (min = max)。而当 x = 1 则 min = 0。
 * 由此可见，x 轴的取值范围是在 max 和 0 之间，所以公式为 x = (max - min) / max
 *
 * 这个时候我们有了 x, y, mixer 这三个参数，自然也就能够反推出 pure 的值了
 * 首先为了减少计算的压力，再加上前面的逻辑经验
 * 纯色的环值 min = 0, max = 255
 * 而另外一个中间值就可以使用2D色彩空间的公式逆推出来：
 * mid = (255 * (x * y - y - x - this.mixer[mid.rgb] / 255 + 1)) / (x * y - x);
 * 至此，倒数第二大问题就 " 顺 利 " 解决了（2022-05-27 22:40 ~ 2022-05-28 05:35）
 * 后面还有一个问题，就是纯色环值的进度条定位跟踪。
 * 思路是...依然是逆推公式...
 */

/* 在三个变量中取出最大的 */
// var max = num1 > num2 ? num1 : num2;
// return max > num3 ? max : num3;
/* 三个变量排序 */
// function compare3rd(a, b, c) {
//   var max, mid, min, up, low, temp;
//   a > b ? ((up = a), (low = b)) : ((up = b), (low = a));
//   up > c ? ((max = up), (temp = c)) : ((max = c), (temp = up));
//   temp > low ? ((mid = temp), (min = low)) : ((mid = low), (min = temp));
//   return { max, mid, min };
// }
